package maple.thelittleredbook.recommendation.service;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import maple.thelittleredbook.content.dto.ContentViewDTO;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 推荐内容缓存服务
 * 负责管理用户个性化推荐内容的Redis缓存
 */
@Slf4j
@Service
public class RecommendationCacheService {

    private final RedisTemplate<String, Object> redisTemplate;

    private static final String RECOMMENDATION_KEY_PREFIX = "recommendation:user:";
    private static final int CACHE_TTL_HOURS = 2; // 缓存过期时间2小时
    private static final int BATCH_SIZE = 10; // 每次缓存的推荐内容数量

    @Autowired
    public RecommendationCacheService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 获取用户的下一个推荐内容
     * 从队列右侧弹出一个内容（FIFO）
     *
     * @param userId 用户ID
     * @return 推荐内容DTO，如果队列为空则返回null
     */
    public ContentViewDTO getNextRecommendation(Long userId) {
        if (userId == null) return null;

        String key = RECOMMENDATION_KEY_PREFIX + userId;

        try {
            // 从队列右侧弹出一个内容（先进先出）
            Object content = redisTemplate.opsForList().rightPop(key);

            if (content != null) {
                ContentViewDTO dto = (ContentViewDTO) content;
                log.debug("从缓存获取推荐内容: userId={}, contentId={}", userId, dto.getId());
                return dto;
            }

            log.debug("用户推荐队列为空: userId={}", userId);
            return null;

        } catch (Exception e) {
            log.error("获取推荐内容失败: userId={}", userId, e);
            // 删除可能损坏的缓存
            deleteRecommendationCache(userId);
            return null;
        }
    }

    /**
     * 批量缓存用户推荐内容
     * 将推荐内容从左侧压入队列，清空原有缓存
     *
     * @param userId 用户ID
     * @param recommendations 推荐内容列表
     */
    public void cacheRecommendations(Long userId, List<ContentViewDTO> recommendations) {
        if (userId == null || recommendations == null || recommendations.isEmpty()) {
            log.warn("缓存推荐内容参数无效: userId={}, recommendations size={}",
                    userId, recommendations != null ? recommendations.size() : "null");
            return;
        }

        String key = RECOMMENDATION_KEY_PREFIX + userId;

        try {
            // 先清除可能存在的旧缓存
            redisTemplate.delete(key);

            // 将推荐内容从左侧批量压入队列
            Object[] contentArray = recommendations.toArray();
            redisTemplate.opsForList().leftPushAll(key, contentArray);

            // 设置过期时间
            redisTemplate.expire(key, CACHE_TTL_HOURS, TimeUnit.HOURS);

            log.debug("缓存用户推荐内容: userId={}, 内容数={}", userId, recommendations.size());

        } catch (Exception e) {
            log.error("缓存推荐内容失败: userId={}", userId, e);
            // 清理可能的部分缓存
            deleteRecommendationCache(userId);
        }
    }

    /**
     * 检查用户推荐队列是否为空
     *
     * @param userId 用户ID
     * @return true-队列为空，false-队列不为空
     */
    public boolean isRecommendationCacheEmpty(Long userId) {
        if (userId == null) return true;

        String key = RECOMMENDATION_KEY_PREFIX + userId;

        try {
            Long size = redisTemplate.opsForList().size(key);
            boolean isEmpty = size == null || size == 0;

            log.debug("检查推荐队列状态: userId={}, isEmpty={}, size={}", userId, isEmpty, size);
            return isEmpty;

        } catch (Exception e) {
            log.error("检查推荐队列状态失败: userId={}", userId, e);
            return true; // 异常时认为队列为空，触发重新计算
        }
    }

    /**
     * 获取用户推荐队列中剩余内容数量
     *
     * @param userId 用户ID
     * @return 剩余内容数量，异常时返回0
     */
    public long getRecommendationCacheSize(Long userId) {
        if (userId == null) return 0;

        String key = RECOMMENDATION_KEY_PREFIX + userId;

        try {
            Long size = redisTemplate.opsForList().size(key);
            long result = size != null ? size : 0;

            log.debug("获取推荐队列大小: userId={}, size={}", userId, result);
            return result;

        } catch (Exception e) {
            log.error("获取推荐队列大小失败: userId={}", userId, e);
            return 0;
        }
    }

    /**
     * 删除用户推荐缓存
     *
     * @param userId 用户ID
     */
    public void deleteRecommendationCache(Long userId) {
        if (userId == null) return;

        String key = RECOMMENDATION_KEY_PREFIX + userId;

        try {
            redisTemplate.delete(key);
            log.debug("删除用户推荐缓存: userId={}", userId);

        } catch (Exception e) {
            log.error("删除用户推荐缓存失败: userId={}", userId, e);
        }
    }

    /**
     * 预热用户推荐缓存
     * 检查缓存是否存在，不存在则返回true表示需要计算推荐
     *
     * @param userId 用户ID
     * @return true-需要计算推荐，false-缓存已存在
     */
    public boolean needsRecommendationCalculation(Long userId) {
        if (userId == null) return false;

        boolean isEmpty = isRecommendationCacheEmpty(userId);

        if (isEmpty) {
            log.debug("用户需要重新计算推荐: userId={}", userId);
        } else {
            log.debug("用户推荐缓存已存在: userId={}", userId);
        }

        return isEmpty;
    }

    /**
     * 刷新用户推荐缓存
     * 强制清空现有缓存，用于用户行为变化后的缓存更新
     *
     * @param userId 用户ID
     */
    public void refreshRecommendationCache(Long userId) {
        if (userId == null) return;

        deleteRecommendationCache(userId);
        log.debug("刷新用户推荐缓存: userId={}", userId);
    }

    /**
     * 批量删除多个用户的推荐缓存
     * 用于系统维护或批量清理
     *
     * @param userIds 用户ID列表
     */
    public void batchDeleteRecommendationCache(List<Long> userIds) {
        if (userIds == null || userIds.isEmpty()) return;

        try {
            for (Long userId : userIds) {
                deleteRecommendationCache(userId);
            }
            log.debug("批量删除推荐缓存完成: 用户数={}", userIds.size());

        } catch (Exception e) {
            log.error("批量删除推荐缓存失败", e);
        }
    }

    /**
     * 获取缓存统计信息
     * 用于监控和调试
     *
     * @param userId 用户ID
     * @return 缓存统计信息
     */
    public CacheStats getCacheStats(Long userId) {
        if (userId == null) {
            return new CacheStats(false, 0, false);
        }

        String key = RECOMMENDATION_KEY_PREFIX + userId;

        try {
            // 检查缓存是否存在
            Boolean exists = redisTemplate.hasKey(key);
            boolean cacheExists = exists != null && exists;

            // 获取队列大小
            long size = cacheExists ? getRecommendationCacheSize(userId) : 0;

            // 检查是否即将过期（剩余时间小于30分钟）
            Long ttl = redisTemplate.getExpire(key, TimeUnit.SECONDS);
            boolean nearExpiry = ttl != null && ttl > 0 && ttl < 1800; // 30分钟

            return new CacheStats(cacheExists, size, nearExpiry);

        } catch (Exception e) {
            log.error("获取缓存统计信息失败: userId={}", userId, e);
            return new CacheStats(false, 0, false);
        }
    }

    /**
     * 缓存统计信息
     */
    @Getter
    public static class CacheStats {
        private final boolean exists;
        private final long size;
        private final boolean nearExpiry;

        public CacheStats(boolean exists, long size, boolean nearExpiry) {
            this.exists = exists;
            this.size = size;
            this.nearExpiry = nearExpiry;
        }

        public boolean isEmpty() {
            return size == 0;
        }
    }
}